//: src/com/silvtar/sync/model/SSAnalyzer.java
/* 
 * Created by 朴晟宁 <piaosn@gmail.com> on 4/21/2013. 
 * Copyright (c) 2013 朴晟宁. All rights reserved.
 * 
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program;
 * 
 * *********************************************************************
 * 
 * Modify History:
 * Date        Version    Description
 * 4/20/2013   0.1        Create the class
 * 4/23/2013   0.2        It can analyze filetrees with MD5 now.
 */

package com.silvtar.sync.model;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;

import com.silvtar.io.StdOut;

/**
 * 分析器类 该类用于创建分析器。对两颗文件树进行对比，将结果放入两条队列中。 本类应当仅当在服务器端时被使用。
 * 
 * @author 朴晟宁/Silvtar
 * @author piaosn@gmail.com
 * @version 0.2
 */
public class SSAnalyzer {

    private SSTree                 localTree  = null; // 要分析的本地树

    private SSTree                 remoteTree = null; // 要分析的远端树

    private LinkedList<SSTreeNode> outQueue   = null; // 传出队列，即需要从local传输到oppo的文件队列

    private LinkedList<SSTreeNode> inQueue    = null; // 传入队列，急需要从oppo传输到local的文件队列

    private boolean                useMd5     = true; // 是否启用MD5对比

    // ---------------------------- getter && setter ---------------------------

    public LinkedList<SSTreeNode> getOutQueue() {

        return outQueue;
    }

    public boolean isUseMd5() {

        return useMd5;
    }

    public void setUseMd5(boolean useMd5) {

        this.useMd5 = useMd5;
    }

    public LinkedList<SSTreeNode> getInQueue() {

        return inQueue;
    }

    // --------------------------------- 主要方法 --------------------------------

    /**
     * @param localTree
     *            本地树，根据本地文件夹所生成的文件数
     * @param remoteTree
     *            远端树，根据远端文件夹所生成的文件数
     */
    public SSAnalyzer(SSTree localTree, SSTree remoteTree) {

        this.localTree = localTree;
        this.remoteTree = remoteTree;

        outQueue = new LinkedList<SSTreeNode>();
        inQueue = new LinkedList<SSTreeNode>();

        if (localTree.isUseMd5() && remoteTree.isUseMd5()) {
            useMd5 = true;
        } else {
            useMd5 = false;
        }
    }

    /**
     * 这个函数是一个入口函数，提供参数调用分析函数。
     * 
     * @return 返回值目前无用途.
     */
    public int analyze() {

        if (useMd5) {
            return analyzeMd5(localTree.getRoot(), remoteTree.getRoot());
        } else {
            return analyze(localTree.getRoot(), remoteTree.getRoot());
        }
    }

    /**
     * 这个函数用于分析两个文件节点，通过对比其中的文件。并把结果加入到两条队列中。 并对于每个子节点递归调用自身。
     * MD5版本，通过MD5校验码来确定文件是否相同，而不是文件属性。
     * 
     * @param currentLocalNode
     *            当前处理的本地文件节点。
     * @param currentOppoNode
     *            当前处理的远端文件节点。
     * @return 返回值目前无用途.
     */
    private int analyzeMd5(SSTreeNode currentLocalNode,
            SSTreeNode currentOppoNode) {

        ArrayList<SSTreeNode> localList = new ArrayList<SSTreeNode>();
        ArrayList<SSTreeNode> oppoList = new ArrayList<SSTreeNode>();

        // 循环将当前目录下的文件放如链表
        for (int i = 0; i < currentLocalNode.getChild().size(); i++) {
            localList.add(currentLocalNode.getChild().get(i));
        }
        for (int i = 0; i < currentOppoNode.getChild().size(); i++) {
            oppoList.add(currentOppoNode.getChild().get(i));
        }

        // 以local链表中的每个元素作为循环
        for (Iterator<SSTreeNode> firIt = localList.iterator(); firIt.hasNext();) {
            boolean found = false;

            SSTreeNode localTreeNode = firIt.next(); // 当前正在查找的元素

            // 在链表2中查找这个元素。并设置found
            if (oppoList.contains(localTreeNode)) {
                found = true;
            }

            // 如果找到了相同的项(这里存在问题，如果fir为dir，sec却是一个文件，那么后面就会出问题)
            if (true == found) {
                SSTreeNode oppoTreeNode = oppoList.get(oppoList
                        .indexOf(localTreeNode));
                // 如果是文件夹
                if (localTreeNode.isDir() && oppoTreeNode.isDir()) {
                    analyzeMd5(localTreeNode, oppoTreeNode);
                    // 同时从两个链表中删除这个元素
                    // 第一个表
                    if (0 == localList.indexOf(localTreeNode)) {
                        localList.remove(localTreeNode);
                        firIt = localList.iterator();
                    } else {
                        localList.remove(localTreeNode);
                    }
                    // 第二个表
                    oppoList.remove(oppoTreeNode);
                }
                // 如果是文件
                else {
                    // 如果MD5一致
                    if (localTreeNode.getMd5().equals(oppoTreeNode.getMd5())) {
                        // 现在可以认为两个文件都是一致的,故把这两个文件从队列中删除
                        // 同时从两个链表中删除这个元素
                        // 第一个表
                        if (0 == localList.indexOf(localTreeNode)) {
                            localList.remove(localTreeNode);
                            firIt = localList.iterator();
                        } else {
                            localList.remove(localTreeNode);
                        }
                        // 第二个表
                        oppoList.remove(oppoTreeNode);
                    }
                    // 如果MD5不一致
                    else {
                        // 如果队列一中文件时间比较新
                        if (localTreeNode.getDate().after(
                                oppoTreeNode.getDate())) {
                            outQueue.add(localTreeNode);
                        }
                        // 如果队列二的比较新
                        else {
                            // 放入队列二的拷贝队列
                            inQueue.add(oppoTreeNode);
                        }
                        // 同时从两个链表中删除这个元素
                        // 第一个表
                        if (0 == localList.indexOf(localTreeNode)) {
                            localList.remove(localTreeNode);
                            firIt = localList.iterator();
                        } else {
                            localList.remove(localTreeNode);
                        }
                        // 第二个表
                        oppoList.remove(oppoTreeNode);
                    }
                }
            }
            // 没找到相同的项
            else {
                outQueue.add(localTreeNode);
                // 删除这个元素
                // 第一个表
                if (0 == localList.indexOf(localTreeNode)) {
                    localList.remove(localTreeNode);
                    firIt = localList.iterator();
                } else {
                    localList.remove(localTreeNode);
                }
            }
        }

        for (Iterator<SSTreeNode> secIt = oppoList.iterator(); secIt.hasNext();) {
            SSTreeNode oppoTreeNode = secIt.next();
            inQueue.add(oppoTreeNode);
            // 删除这个元素
            if (0 == oppoList.indexOf(oppoTreeNode)) {
                oppoList.remove(oppoTreeNode);
                secIt = oppoList.iterator();
            } else {
                oppoList.remove(oppoTreeNode);
            }
        }
        return 0;
    }

    /**
     * 这个函数用于分析两个文件节点，对比其中的文件。并把结果加入到两条队列中。 并对于每个子节点递归调用自身。
     * 
     * @param currentLocalNode
     *            当前处理的本地文件节点。
     * @param currentOppoNode
     *            当前处理的远端文件节点。
     * @return 返回值目前无用途.
     */
    private int analyze(SSTreeNode currentLocalNode, SSTreeNode currentOppoNode) {

        ArrayList<SSTreeNode> localList = new ArrayList<SSTreeNode>();
        ArrayList<SSTreeNode> oppoList = new ArrayList<SSTreeNode>();

        // 循环将当前目录下的文件放如链表
        for (int i = 0; i < currentLocalNode.getChild().size(); i++) {
            localList.add(currentLocalNode.getChild().get(i));
        }
        for (int i = 0; i < currentOppoNode.getChild().size(); i++) {
            oppoList.add(currentOppoNode.getChild().get(i));
        }

        // 以local链表中的每个元素作为循环
        for (Iterator<SSTreeNode> firIt = localList.iterator(); firIt.hasNext();) {
            boolean found = false;

            SSTreeNode localTreeNode = firIt.next(); // 当前正在查找的元素

            // 在链表2中查找这个元素。并设置found
            if (oppoList.contains(localTreeNode)) {
                found = true;
            }

            // 如果找到了相同的项(这里存在问题，如果fir为dir，sec却是一个文件，那么后面就会出问题)
            if (true == found) {
                SSTreeNode oppoTreeNode = oppoList.get(oppoList
                        .indexOf(localTreeNode));
                // 如果是文件夹
                if (localTreeNode.isDir() && oppoTreeNode.isDir()) {
                    analyze(localTreeNode, oppoTreeNode);
                    // 同时从两个链表中删除这个元素
                    // 第一个表
                    if (0 == localList.indexOf(localTreeNode)) {
                        localList.remove(localTreeNode);
                        firIt = localList.iterator();
                    } else {
                        localList.remove(localTreeNode);
                    }
                    // 第二个表
                    oppoList.remove(oppoTreeNode);
                }
                // 如果不是文件夹
                else {
                    // 如果修改时间一致
                    if (localTreeNode.getDate().equals(oppoTreeNode.getDate())) {
                        // 如果文件大小一致
                        if (localTreeNode.getSize() == oppoTreeNode.getSize()) {
                            // 现在可以认为两个文件都是一致的,故把这两个文件从队列中删除
                            // 同时从两个链表中删除这个元素
                            // 第一个表
                            if (0 == localList.indexOf(localTreeNode)) {
                                localList.remove(localTreeNode);
                                firIt = localList.iterator();
                            } else {
                                localList.remove(localTreeNode);
                            }
                            // 第二个表
                            oppoList.remove(oppoTreeNode);
                        }
                    }
                    // 如果修改时间不一致
                    else {
                        // 如果队列一中文件时间比较新
                        if (localTreeNode.getDate().after(
                                oppoTreeNode.getDate())) {
                            outQueue.add(localTreeNode);
                        }
                        // 如果队列二的比较新
                        else {
                            // 放入队列二的拷贝队列
                            inQueue.add(oppoTreeNode);
                        }
                        // 同时从两个链表中删除这个元素
                        // 第一个表
                        if (0 == localList.indexOf(localTreeNode)) {
                            localList.remove(localTreeNode);
                            firIt = localList.iterator();
                        } else {
                            localList.remove(localTreeNode);
                        }
                        // 第二个表
                        oppoList.remove(oppoTreeNode);
                    }
                }
            }
            // 没找到相同的项
            else {
                outQueue.add(localTreeNode);
                // 删除这个元素
                // 第一个表
                if (0 == localList.indexOf(localTreeNode)) {
                    localList.remove(localTreeNode);
                    firIt = localList.iterator();
                } else {
                    localList.remove(localTreeNode);
                }
            }

        }

        for (Iterator<SSTreeNode> secIt = oppoList.iterator(); secIt.hasNext();) {
            SSTreeNode oppoTreeNode = secIt.next();
            inQueue.add(oppoTreeNode);
            // 删除这个元素
            if (0 == oppoList.indexOf(oppoTreeNode)) {
                oppoList.remove(oppoTreeNode);
                secIt = oppoList.iterator();
            } else {
                oppoList.remove(oppoTreeNode);
            }
        }
        return 0;
    }

    /**
     * 打印函数，向输出设备中输出两个传输队列的内容。这个函数会清空两个队列中的内容。
     * 
     */
    public void print() {

        StdOut.println("OUT_QUEUE:  size = " + outQueue.size() + "\n");
        while (!outQueue.isEmpty()) {
            StdOut.println("  " + outQueue.pop().getName());
        }

        StdOut.println();
        StdOut.println("IN_QUEUE:  size = " + inQueue.size() + "\n");
        while (!inQueue.isEmpty()) {
            StdOut.println("  " + inQueue.pop().getName());
        }
    }
}
